#include "Pow.h"
#include "Sym.h"
#include "Number.h"
#include "functions.h"
#include "operators.h"
namespace sympycpp {
Pow::Pow(const Pow & p) : Ex(POW) {
	base_ = p.base_->copy();
	exponent_ = p.exponent_->copy();
	sign(p.sign());
}
Pow::Pow(const Ex * b, const Ex * e) : Ex(POW) {
	base_ = b->copy();
	exponent_ = e->copy();
	sign(plus);
	if ((exponent_->isNum() && exponent_->sign() == minus) && (base_->isNum() && ((Number *)base_)->eq(0))) {
		throw ZeroDivision();
	}

}
Pow::Pow(Ex * & b, Ex * & e, const allocationPolicy flag) : Ex(POW) {
	sign(plus);
	base_ = b;
	exponent_ = e;
	b = e = 0;
	if ((exponent_->isNum() && exponent_->sign() == minus) && (base_->isNum() && ((Number *)base_)->eq(0))) {
		b = base_;
		e = exponent_;
		base_ = exponent_ = 0;
		throw ZeroDivision();
	}
}
Pow::~Pow(){
	delete base_;
	delete exponent_;
}

bool Pow::isExponentInteger() const {
	return exponent_->isInteger();
}
bool Pow::isExponentPositive() const {
	return exponent_->sign();
}
std::string Pow::treeView(const int i) const {
	std::ostringstream stream;
	spaces(i, stream);
	char ch = sign() ? '+' : '-';
	stream << "> (" << ch << ")" << "POW"<<std::endl;
	stream << base_->treeView(i+1);
	stream << exponent_->treeView(i+1);
	return stream.str();
}
std::string Pow::abs_Exp_str() const {
	if (exponent_->isNum() && ((Number*)exponent_)->abs().eq(1)) {
		if (base_->isAdd() || base_->isMul()) {
			return "(" + base_->str() + ")";
		}
		else {
			return base_->str();
		}
	}
	std::string s(base_->str());
	if (base_->size() > 1) {
		s = "(" + s + ")";
	}
	if (exponent_->size() > 1) {
		s += "^(" + exponent_->unsigned_str() + ")";
	}
	else {
		s += "^" + exponent_->unsigned_str();
	}
	if (!sign()) {
		return "-(" + s + ")";
	}
	return s;
}
size_t Pow::size() const {
	 return 2;
 }
size_t Pow::asize() const {
	 return 2;
 }
size_t Pow::rsize(const bool all) const {
	 return base_->rsize(false) + exponent_->rsize(false);
 }
Ex * Pow::copy() const {
	return new Pow(*this);
}
std::string Pow::str() const {
	if (exponent_->isInteger() && ((Number *)exponent_)->eq(-1)) {
		if (sign()) {
			return "1/"+base_->str();
		}
		else {
			return "-1/"+base_->str();
		}
	}
	if (sign()) {
		return unsigned_str();
	}
	else {
		return "-(" + unsigned_str() + ")";
	}
}
std::string Pow::unsigned_str() const {
	std::string s(base_->str());
	if (base_->size() > 1) {
		s = "(" + s + ")";
	}
	if (exponent_->size() > 1) {
		s += "^(" + exponent_->str() + ")";
	}
	else {
		s += "^" + exponent_->str();
	}
	return s;
}
Ex * Pow::copyInverted() const {
	if (exponent_->isInteger()) {
		if(((Number *)exponent_)->eq(-1)) {
			Ex * newExp = base_->copy();
			newExp->sign(newExp->sign() == this->sign());
			return newExp;
		}
	}
	Ex * newExp = copy();
	((Pow *)newExp)->exponent_->sign(!exponent_->sign());
	return newExp;
}
Ex * Pow::diff(const Sym & x) const {
	if (exponent_->isNum()) {
		if (base_->isSym()) {
			if(((Sym *)base_)->title() == x.title()) { // [X^number]' --> number*X^(number-1)
				Number * numExp = (Number *)exponent_;
				Number n(0);
				n.setToSubtraction(*numExp, 1); // n-1
				Ex * newPow = Operations::power(base_, &n); // x^(n-1)
				Ex * multiplicity = exponent_->copy(); // n
				Ex * df = Operations::multiplication(multiplicity, newPow, STEALING); //nx^(n-1)
				df->sign(this->sign() == df->sign()); //
				return df;
			}
			else { // [y^number]' (by x) -->  0
				return new Number(0);
			}
		}
		else { // [f(x)^number]' --> number*f(x)^(number-1)*f'(x)
			if (isExponentPositive()) {
				if (((Number *)exponent_)->eq(2)) { // f(x)^2 --> 2f(x)f'(x)
					Ex * two = new Number(2);
					two->sign(this->sign());
					Ex * df = base_->diff(x);
					Ex * f = base_->copy();
					f = Operations::multiplication(df, f, STEALING);
					return Operations::multiplication(two, f,STEALING); 
				}
				else {// f(x)^n for n > 2 --> nf(x)^(n-1)*f'(x)
					Ex * fxn_1 =  copy();// f(x)^(n-1) ; sign of primal power is copying too
					((Number *)((Pow *)fxn_1)->exponent_)->setToSubtraction(*(Number *)exponent_, 1);
					Ex * multiplicity = exponent_->copy();
					Ex * df = base_->diff(x);
					fxn_1 = Operations::multiplication(df, fxn_1,STEALING);
					return Operations::multiplication( multiplicity, fxn_1, STEALING);

				}
			}
			else {
				if (((Number *)exponent_)->eq(-1)) { // f(x)^-1 --> -f'(x)*f(x)^-2
					Ex * df = base_->diff(x);
					Ex * f_2 = copy();// sign of primal power is copying too
					((Number *)((Pow *)f_2)->exponent_)->setToSubtraction(*(Number *)exponent_, 1);
					Ex * tmp = Operations::multiplication(df, f_2,STEALING); 
					tmp->sign(tmp->sign() == minus);
					return tmp;
				}
				else {// f(x)^-n for -n < -1 --> -nf(x)^(-n-1)*f'(x)
					Ex * fxn_1 =  copy();// f(x)^(n-1) ; sign of original power is copy too
					((Number *)((Pow *)fxn_1)->exponent_)->setToSubtraction(*(Number *)exponent_, 1);
					Ex * multiplicity = exponent_->copy();
					Ex * df = base_->diff(x);
					fxn_1 = Operations::multiplication(df, fxn_1,STEALING);
					Ex * tmp = Operations::multiplication( multiplicity, fxn_1, STEALING);
					return tmp;
				}
			}
		}
	}
	if (exponent_->isSym() && ((Sym *)exponent_)->title() == x.title()) { // [a^x]' --> a^x*ln(a)
		if (base_->isNum() || (base_->isSym() && ((Sym *)base_)->title() != x.title())) {
			Ex * cpBase = base_->copy();
			Ex * LnA = Ln_(cpBase, STEALING);
			LnA->sign(exponent_->sign()); // [a^(-x)]' --> a^(-x)*(-ln(a))
			Ex * newPow = this->copy();		
			return Operations::multiplication(newPow, LnA, STEALING);
		}
	} // [f(x)^g(x)]' --> f(x)^g(x) * {g'(x) * ln(f(x) + (g(x)/f(x)) * f'(x)}
	Ex * p1 = base_->diff(x); // f'
	Ex * p2 = exponent_->diff(x); // g'
	Ex * p3 = this->copy(); // f ^ g
	Ex * copyBase = base_->copy(); // f
	Ex * p4 = Ln_(copyBase, STEALING); //ln(f)
	Number in(-1); //-1
	Ex * p5 = Operations::power(base_, &in);	// 1/f
	Ex * p6 = exponent_->copy();			// g
	p2 = Operations::multiplication(p2, p4, STEALING);// g' * ln(f)
	p4 = Operations::multiplication(p5, p6, STEALING);// g / f
	p6 = Operations::multiplication(p4, p1, STEALING);// g/f * f' 
	p1 = Operations::addition(p2, p6, STEALING); // g'*ln(f) + f'g/f
	p2 = Operations::multiplication(p3, p1, STEALING); // f^g (g'ln(f) + (f'g/f)
	return p2;

}
void Pow::ganef(Ex * & b, Ex * & e, const allocationPolicy flag) {
	b = base_;
	e = exponent_;
	base_ = exponent_ = 0;
}
}
